package com.liyao.storehouse.api.utill;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA 加解密算法
 * @author WeiNing
 * @since 2023.6.30
 */
@Component
public class RSAUtils {

    //密钥类实例化入参
    @Value("${rsa.key-algorithm:RSA}")
    public String KEY_ALGORITHM;

    //密钥对中公钥映射key
    @Value("${rsa.public-key:RSAPublicKey}")
    private String PUBLIC_KEY;

    //密钥对中私钥映射key
    @Value("${rsa.private-key:RSAPrivateKey}")
    private String PRIVATE_KEY;

    //签名类实例化入参
    @Value("${rsa.signature-algorithm:MD5withRSA}")
    public String SIGNATURE_ALGORITHM;


    //初始化密钥对生成器时，指定密钥大小的整数值
    //1024 bits 的 RSA 密钥对: 公钥长度216, 私钥长度1624, 加密后密文长度172
    @Value("${rsa.key-pair-init-size:1024}")
    private int KEY_PAIR_INIT_SIZE;
    //最大加密明文大小
    private int MAX_ENCRYPT_BLOCK = 117;
    //最大解密密文大小
    private int MAX_DECRYPT_BLOCK = 128;

    //2048 bits 的 RSA 密钥对: 公钥长度392, 私钥长度848, 加密后密文长度344
    //private int KEY_PAIR_INIT_SIZE = 2048;
    //最大加密明文大小
    //private int MAX_ENCRYPT_BLOCK = 245;
    //最大解密密文大小
    //private int MAX_DECRYPT_BLOCK = 256;

    //生成密钥对
    public Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);

        // 设置密钥对的 bit 数，越大越安全
        keyPairGen.initialize(KEY_PAIR_INIT_SIZE);
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 获取公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    // 获取公钥字符串
    public String getPublicKeyStr(Map<String, Object> keyMap) {
        // 获得 map 中的公钥对象，转为 key 对象
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    }

    // 获取私钥字符串
    public String getPrivateKeyStr(Map<String, Object> keyMap) {
        // 获得 map 中的私钥对象，转为 key 对象
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    }

    // 获取公钥
    private PublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    // 获取私钥
    private PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * BASE64 编码返回加密字符串
     * @param key 需要编码的字节数组
     * @return 编码后的字符串
     */
    private String encryptBASE64(byte[] key) {
        return new String(Base64.getEncoder().encode(key));
    }

    /**
     * BASE64 解码，返回字节数组
     * @param key 待解码的字符串
     * @return 解码后的字节数组
     */
    private byte[] decryptBASE64(String key) {
        return Base64.getDecoder().decode(key);
    }

    /**
     * 公钥加密
     * @param text 待加密的明文字符串
     * @param publicKeyStr 公钥
     * @return 加密后的密文
     */
    public String encrypt(String text, String publicKeyStr) {
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
            byte[] tempBytes = cipher.doFinal(text.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(tempBytes);
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e);
        }
    }

    /**
     * 私钥解密
     * @param secretText 待解密的密文字符串
     * @param privateKeyStr 私钥
     * @return 解密后的明文
     */
    public String decrypt(String secretText, String privateKeyStr) {
        try {
            // 生成私钥
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
            // 密文解码
            byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes("UTF-8"));
            byte[] tempBytes = cipher.doFinal(secretTextDecoded);
            return new String(tempBytes);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e);
        }
    }

    /**
     * 用私钥对信息生成数字签名
     * @param data       加密数据
     * @param privateKey 私钥
     * @return java.lang.String
     **/
    public String sign(byte[] data, String privateKey) throws Exception {
        // 解密由base64编码的私钥
        byte[] keyBytes = decryptBASE64(privateKey);
        // 构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名
     * @param data      加密数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     */
    public boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        // 解密由base64编码的公钥
        byte[] keyBytes = decryptBASE64(publicKey);
        // 构造X509EncodedKeySpec对象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取公钥匙对象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        // 验证签名是否正常
        return signature.verify(decryptBASE64(sign));
    }

    /**
     * 用私钥加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
        // 对密钥解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    public static void main(String[] args) throws Exception {
        RSAUtils rsa = new RSAUtils();
//         解密
        String plainText = rsa.decrypt("d361+YtYudSUoPwtvZd700MRTY8i6S3j3uqFB36A4QsZnwJPMLwTnPoAV8PL4HuXB84shagD2eA2IAo3SuZ14zGQao9EfuAdax+WtmHFlDaPHR9HncADcVNBwUdz+CFbYCsIFqwLbfOEun5d1MKP2rDYmt43ncpSNXr02j69cpE=", SystemConfig.RSA_PRIVATE_KEY);
        System.out.println(String.format("解密后明文:[%s]", plainText));
//        //生成密钥对
//        Map<String, Object> keyMap = rsa.initKey();
//        //生成密钥对
//        String publicKey = rsa.getPublicKeyStr(keyMap);
//        System.out.println(String.format("公钥:[%s]，长度:[%s]", publicKey, publicKey.length()));
//        //生成密钥对
//        String privateKey = rsa.getPrivateKeyStr(keyMap);
//        System.out.println(String.format("私钥:[%s]，长度:[%s]", privateKey, privateKey.length()));
//
//        // 原始明文
//        String content = "admin";
//        // 加密
//        String cipherText = rsa.encrypt(content, publicKey);
//        System.out.println(String.format("加密后的密文:[%s]，长度:[%s]", cipherText, cipherText.length()));
//
//        // 解密
//        String plainText = rsa.decrypt(cipherText, privateKey);
//        System.out.println(String.format("解密后明文:[%s]", plainText));
//
//        //产生签名
//        String signStr = "password";
//        byte[] encodedData = rsa.encryptByPrivateKey(signStr.getBytes(), privateKey);
//        String sign = rsa.sign(encodedData, privateKey);
//        System.out.println(String.format("签名:[%s]", sign));
//
//        // 验证签名
//        boolean status = rsa.verify(encodedData, publicKey, sign);
//        System.out.println(String.format("状态:[%s]", status));
    }
}
